﻿using System;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Text;
using System.Collections;
using GameServer.Virtual_Objects.User;
using GameServer.Virtual_Objects.Room;
using GameServer.Managers;
namespace GameServer
{
    class UDPServer
    {
        // private UdpClient UDPSocket_1;
        // private UdpClient UDPSocket_2;

        ~UDPServer()
        {
            GC.Collect();
        }
        IPEndPoint GroupEP5350;
        IPEndPoint GroupEP5351;
        // private IPEndPoint[] usersUDP = new IPEndPoint[500];

        public bool StartUDPServer()
        {
            Thread RecvThread1 = new Thread(new ThreadStart(RecvUDP1));
            RecvThread1.Start();
            Thread RecvThread2 = new Thread(new ThreadStart(RecvUDP2));
            RecvThread2.Start();
            return true;
        }

        private void RecvUDP1()
        {
            byte[] btReceiveData;
            IPAddress GroupIP;
            GroupIP = IPAddress.Parse(Config.SERVER_IP);
            GroupEP5350 = new IPEndPoint(GroupIP, 5350);
            UdpClient UDPSocket_1 = new UdpClient(5350);
            try
            {
                while (true)
                {
                    try
                    {
                        btReceiveData = UDPSocket_1.Receive(ref GroupEP5350);
                        byte[] Response = AnalyzePacket(UDPSocket_1, btReceiveData, GroupEP5350, 5350);
                        if (Response != null)
                        {
                            //UDPSocket_1.Send(Response, Response.Length, GroupEP5350);
                            UDPSocket_1.BeginSend(Response, Response.Length, GroupEP5350, SendProc, UDPSocket_1);
                        }
                    }
                    catch (Exception ex)
                    {
                        //Log.WriteError("Error at UDP1 try: " + ex.Message);
                    }
                }
            }
            catch (Exception ex)
            {
                //Log.WriteError("Error at UDP1: " + ex.Message);
            }
        }

        private void RecvUDP2()
        {
            byte[] btReceiveData;
            IPAddress GroupIP;
            GroupIP = IPAddress.Parse(Config.SERVER_IP);
            GroupEP5351 = new IPEndPoint(GroupIP, 5351);
            UdpClient UDPSocket_2 = new UdpClient(5351);
            try
            {
                while (true)
                {
                    try
                    {
                        btReceiveData = UDPSocket_2.Receive(ref GroupEP5351);
                        byte[] Response = AnalyzePacket(UDPSocket_2, btReceiveData, GroupEP5351, 5351);
                        //UDPSocket_2.Send(Response, Response.Length, GroupEP5351);
                        UDPSocket_2.BeginSend(Response, Response.Length, GroupEP5351, SendProc, UDPSocket_2);
                    }
                    catch (Exception ex)
                    {
                        //Log.WriteError("Error at UDP2: " + ex.Message);
                    }
                }
            }
            catch (Exception ex)
            {
                //Log.WriteError("Error at UDP2: " + ex.Message);
            }
        }
        void SendProc(IAsyncResult t)
        {
            try
            {
                UdpClient a = (UdpClient)t.AsyncState;
                a.EndSend(t);
            }
            catch (Exception ex)
            {
                Log.WriteError("Error at SendProc: " + ex.Message);
            }

        }
        private byte[] intToByteArray(int value)
        {
            return System.BitConverter.GetBytes(value);
        }
        private byte[] AnalyzePacket(UdpClient UDPClient, byte[] RecvPacket, IPEndPoint IPeo, int port)
        {
            try
            {
                byte[] Response = new Byte[1] { 0x00 };
                if (RecvPacket.Length > 5)
                {
                    if (RecvPacket[0] == 0x10 && RecvPacket[1] == 0x10 && RecvPacket[2] == 0x00 && RecvPacket[3] == 0x00 && RecvPacket[14] == 0x21) // IP Packet
                    {
                        String[] exc = IPeo.Address.ToString().Split('.'); // IP
                        int b1 = ((byte)Int32.Parse(exc[0])) ^ 0x11;
                        int b2 = ((byte)Int32.Parse(exc[1])) ^ 0x11;
                        int b3 = ((byte)Int32.Parse(exc[2])) ^ 0x11;
                        int b4 = ((byte)Int32.Parse(exc[3])) ^ 0x11;
                        byte[] IPBytes = IPeo.Address.GetAddressBytes();
                        byte[] PortBytes = BitConverter.GetBytes(IPeo.Port);

                        byte[] LocalIPBytes = new byte[4] { RecvPacket[34], RecvPacket[35], RecvPacket[36], RecvPacket[37] };

                        LocalIPBytes[0] = Convert.ToByte(LocalIPBytes[0] ^ 0xC3);
                        LocalIPBytes[1] = Convert.ToByte(LocalIPBytes[1] ^ 0xC3);
                        LocalIPBytes[2] = Convert.ToByte(LocalIPBytes[2] ^ 0xC3);
                        LocalIPBytes[3] = Convert.ToByte(LocalIPBytes[3] ^ 0xC3);

                        byte[] LocalPortBytes = new byte[2] { RecvPacket[32], RecvPacket[33] };

                        LocalPortBytes[0] = Convert.ToByte(LocalPortBytes[0] ^ RecvPacket[0]);
                        LocalPortBytes[1] = Convert.ToByte(LocalPortBytes[1] ^ RecvPacket[0]);

                        IPEndPoint LocalIPeo = new IPEndPoint(new IPAddress(BitConverter.ToUInt32(LocalIPBytes, 0)), BitConverter.ToUInt16(LocalPortBytes, 0));

                        byte[] SessionIDBytes = new byte[2] { RecvPacket[5], RecvPacket[4] };
                        ushort SessionID = BitConverter.ToUInt16(SessionIDBytes, 0);

                        virtualUser Target = UserManager.getTargetUser(SessionID);
                        if (Target != null)
                            Target.setLocalEndPoint(LocalIPeo); // LocalIP

                        Response = new Byte[65] 
                        {
                            0x10, 0x10, 0, 0, RecvPacket[4], RecvPacket[5],
                            0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x41,
                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x13, 0x11,
                            (byte)(RecvPacket[32]^0x54), (byte)(RecvPacket[33]^0x54), (byte)b1, (byte)b2, (byte)b3, (byte)b4,
                            0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x01, 0x11, 0x13, 0x11, 
                            (byte)(RecvPacket[32]^0x54), (byte)(RecvPacket[33]^0x54), (byte)(RecvPacket[34]^0x54), (byte)(RecvPacket[35]^0x54), (byte)(RecvPacket[36]^0x54), (byte)(RecvPacket[37]^0x54), 
                            0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x11
                        };
                    }
                    else if (RecvPacket[0] == 0x10 && RecvPacket[1] == 0x01 && RecvPacket[2] == 0x01)
                    {
                        int tID = (RecvPacket[RecvPacket.Length - 4] << 24) | (RecvPacket[RecvPacket.Length - 3] << 16) | (RecvPacket[RecvPacket.Length - 2] << 8) | RecvPacket[RecvPacket.Length - 1];

                        byte[] SessionIDBytes = new byte[2] { RecvPacket[5], RecvPacket[4] };
                        ushort SessionID = BitConverter.ToUInt16(SessionIDBytes, 0); // Session bytes
                        //usersUDP[tID] = IPeo;

                        Response = new Byte[14] { 0x10, 0x01, 0x01, 0x00, 0x14, 0xe7, 0x00, 0x00, 0x00, 0x00,
                        RecvPacket[RecvPacket.Length - 4], 
                        RecvPacket[RecvPacket.Length - 3],
                        RecvPacket[RecvPacket.Length - 2], 
                        RecvPacket[RecvPacket.Length - 1] };

                        virtualUser Client = UserManager.getUser(tID);
                        Client.RemoteNetwork = IPeo;
                        Client.setRemoteEndPoint(IPeo);
                    }
                    else if (RecvPacket[14] == 0x31) // Tunneling - If uncomment it, 999 will works but nades invi
                    {
                        /*
                        byte[] SessionIDBytes = new byte[2] { RecvPacket[5], RecvPacket[4] };
                        ushort SessionID = BitConverter.ToUInt16(SessionIDBytes, 0);

                        ushort RoomID = BitConverter.ToUInt16(RecvPacket, 7);
                        int Channel = 1;
                        virtualUser Target = UserManager.getTargetUser(SessionID);

                        if (Target != null)
                        {
                            Channel = Target.Channel;

                            Virtual_Objects.Room.virtualRoom Room = RoomManager.getRoom(Channel, Convert.ToInt32(RoomID));
                            if (Room != null)
                            {
                                if (Room.PlayerCount > 1)
                                {
                                    foreach (virtualUser Player in Room.Players)
                                    {
                                        UDPClient.Send(RecvPacket, RecvPacket.Length, Player.remoteEndPoint);
                                    }
                                    foreach (virtualUser Spectator in Room.Spectators)
                                    {
                                        UDPClient.Send(RecvPacket, RecvPacket.Length, Spectator.remoteEndPoint);
                                    }
                                }
                            }
                        }
                        Response = RecvPacket;*/
                    }

                    /*else if (RecvPacket[0] == 0x10 && RecvPacket[1] == 0x10 && RecvPacket[2] == 0x00 && RecvPacket[3] == 0x00 && RecvPacket[14] == 0x31)
                    {
                        //int id = (int)RecvPacket[23];
                        int id = (int)((RecvPacket[22]) << 8) | (RecvPacket[23]);
                        //cl.Send(RecvPacket, RecvPacket.Length, (IPEndPoint)usersUDP[id]);

                        cl.BeginSend(RecvPacket, RecvPacket.Length, (IPEndPoint)usersUDP[id], SendProc, (object)cl);
                        return null;
                    }*/
                }
                return Response;
            }
            catch (Exception ex)
            {
                return new Byte[1] { 0x00 };
            }
        }
    }
}
